'''
* This is the projet for Brtc LlmOps Platform
* @Author Leon-liao <liaosiliang@alltman.com>
* @Description //TODO 
* @File: api_tool_service.py
* @Time: 2025/9/23
* @All Rights Reserve By Brtc
'''
import json
import uuid
from dataclasses import dataclass
from typing import Any

from injector import inject
from sqlalchemy.sql.expression import desc
from internal.model import ApiToolProvider, ApiTool, Account
from internal.core.tools.api_tools.provider import ApiProviderManager
from internal.core.tools.api_tools.entities.openapi_schema import OpenAPISchema
from internal.exception.exception import ValidateErrorException, NotFoundException
from internal.schema.api_tool_schema import CreateApiToolReq, GetApiToolProvidersWithPageReq, UpdateApiToolProviderReq
from pkg.paginator.paginator import Paginator
from pkg.sqlachemy.sqlalchemy import SQLAlchemy
from .base_service import BaseService

@inject
@dataclass
class ApiToolService(BaseService):
    db:SQLAlchemy
    api_tool_provider_manager:ApiProviderManager
    def update_api_tool_provider(self, provider_id:uuid.UUID, req:UpdateApiToolProviderReq, account:Account):
        """根据传入的provider_id + 新的工具 完成工具在数据库中的修改"""

        #1、 根据传入的provider_id 查找API工具提供者信息并校验
        api_tool_provider = self.get(ApiToolProvider, provider_id)
        if api_tool_provider is None or api_tool_provider.account_id == account.id:
            raise ValidateErrorException("需要修改的工具不存在")
        #2、校验openapi_schema
        openapi_schema = self.parse_openapi_schema(req.openapi_schema.data)
        #3、检测当前账号是否已经创建了同名的工具提供者， 如果是则抛出 错误
        check_api_tool_provider = self.db.session.query(ApiToolProvider).filter(
            ApiToolProvider.account_id == account.id,
            ApiToolProvider.name == req.name.data,
            ApiToolProvider.id !=api_tool_provider.id
        ).one_or_none()
        if check_api_tool_provider:
            raise ValidateErrorException(f"该工具提供者名字{req.name.data}已经存在")
        #4、开启数据的自动提交
        with self.db.auto_commit():
            #5、先删除工具提供者下面的所有工具
            self.db.session.query(ApiTool).filter(
                ApiTool.provider_id == api_tool_provider.id,
                ApiTool.account_id == account.id,
            ).delete()
        # 6、修改工具提供信息
        self.update(
            api_tool_provider,
            name = req.name.data,
            icon=req.icon.data,
            headers = req.headers.data,
            description = openapi_schema.description,
            openapi_schema = req.openapi_schema.data
        )
        #7、新增工具完成更新
        for path, path_item in openapi_schema.paths.items():
            for method, method_item in path_item.items():
                self.create(
                    ApiTool,
                    account_id=account.id,
                    provider_id=api_tool_provider.id,
                    name = method_item.get("operationId"),
                    description=method_item.get("description"),
                    url=f"{openapi_schema.server}{path}",
                    method=method,
                    parameters=method_item.get("parameters",[])
                )






    def get_api_tool_providers_with_page(self, req: GetApiToolProvidersWithPageReq, account:Account) -> tuple[list[Any], Paginator]:
        """从数据库里面获取API工具服务商分页信息"""
        #1、构建分页器 查询
        paginator = Paginator(db=self.db, req=req)
        #2、构建筛选器
        filters = [ApiToolProvider.account_id == account.id]

        if req.search_word.data:
            """添加search_word到筛选器中进行模糊查询"""
            filters.append(ApiToolProvider.name.ilike(f"%{req.search_word.data}%"))
        #3、执行分页并获取数据
        api_tool_providers = paginator.paginate(
            self.db.session
            .query(ApiToolProvider)
            .filter(*filters)
            .order_by(desc("created_at"))
        )

        return api_tool_providers, paginator



    def delete_api_tool_provider(self, provider_id:uuid.UUID, account:Account):
        """根据 传入的 provider id 删除对应的API供提供商+工具的 数据库记录"""
        #1、先找到 数据，检查 provider_id对应的 提供杀你是否存在
        api_tool_provider = self.get(ApiToolProvider, provider_id)
        if api_tool_provider is None or api_tool_provider.account_id != account.id:
            raise NotFoundException("该API工具提供商不存在， 请检查！")
        #2、先删除工具
        with self.db.auto_commit():
            self.db.session.query(ApiTool).filter(
                ApiTool.provider_id == provider_id,
                ApiTool.account_id == account.id,
            ).delete()

            # 3、再来删除服务提供者
            self.db.session.delete(api_tool_provider)


    def get_api_tool(self, provider_id:uuid.UUID, tool_name:str, account:Account)->ApiTool:
        """根据传入的provider_id + tool_id 查询数据库里面的数据"""
        api_tool = self.db.session.query(ApiTool).filter_by(
            provider_id=provider_id,
            name=tool_name,
        ).one_or_none()

        if api_tool is None or api_tool.account_id != account.id:
            raise NotFoundException(f"工具 {tool_name} 不存在 请仔细检查！")

        return api_tool


    def get_api_tool_provider(self, provider_id:uuid.UUID, account:Account)->ApiToolProvider:
        """根据 传递的provider_id 获取工具提供者信息"""
        #1、查询数据库获取对应的数据
        api_tool_provider = self.get(ApiToolProvider, provider_id)

        #2、检验是否数据为空, 并且判断该数据是否属于当前账号
        if api_tool_provider is None or api_tool_provider.account_id != account.id:
            raise NotFoundException("该工具提供商不存在！")

        return api_tool_provider



    def create_api_tool(self, req: CreateApiToolReq, account:Account)->None:
        """根据传递的请求创建自定义API工具"""
        # 1、检验并提取openapi_schema对应的数据
        openapi_schema = self.parse_openapi_schema(req.openapi_schema.data)
        #2、查询当前登录的账号是否已经创建的同名的工具提供者， 如果是则抛出错误
        api_tool_provider = self.db.session.query(ApiToolProvider).filter_by(
            account_id=account.id,
            name=req.name.data
        ).one_or_none()

        if api_tool_provider:
            raise ValidateErrorException(f"该工具名字{req.name.data}已经存在")
        #3、首先要创建工具提者， 再创建工具
        api_tool_provider = self.create(
            ApiToolProvider,
            account_id=account.id,
            name = req.name.data,
            icon = req.icon.data,
            description = openapi_schema.description,
            openapi_schema=req.openapi_schema.data,
            headers=req.headers.data
        )

        #4、创建api工具并关联api_tool_provider
        for path, path_item in openapi_schema.paths.items():
            for method, method_item in path_item.items():
                self.create(
                    ApiTool,
                    account_id=account.id,
                    provider_id=api_tool_provider.id,
                    name = method_item.get("operationId"),
                    description=method_item.get("description"),
                    url=f"{openapi_schema.server}{path}",
                    method=method,
                    parameters=method_item.get("parameters",[])
                )


    @classmethod
    def parse_openapi_schema(self, openapi_schema: str)->OpenAPISchema:
        """解析传递的Openapi_schema字符串，是否符合规范如不符合则抛出异常"""
        try:
            data = json.loads(openapi_schema.strip())
            if not isinstance(data, dict):
                raise
        except Exception as e:
            raise ValidateErrorException("传递的数据必须是符合OpenAPI规范的JSON字符串")

        return OpenAPISchema(**data)

    def api_tool_invoke(self):
        provider_id = "213bca44-3f93-43ac-bd19-fad79c2b1ec9"
        tool_name = "YoudaoSuggest"
        api_tool = self.db.session.query(ApiTool).filter(
            ApiTool.provider_id == provider_id,
            ApiTool.name == tool_name
        ).one_or_none()
        api_tool_provider = api_tool.provider
        from internal.core.tools.api_tools.entities.tool_entity import ToolEntity
        tool = self.api_tool_provider_manager.get_tool(ToolEntity(
            id = provider_id,
            name=tool_name,
            url=api_tool.url,
            method=api_tool.method,
            description=api_tool.description,
            headers=api_tool_provider.headers,
            parameters=api_tool.parameters,
        ))

        return tool.invoke({"q":"love", "doctype":"json"})